﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Mono.Cecil.PE;

namespace NHook
{
	public class VA
	{
		public VA(UInt32 address)
		{
			this.Address = address;
		}
		public VA(IntPtr address)
		{
			this.Address = (UInt32)address.ToInt32();
		}
		public UInt32 Address;
		public RVA ToRVA(Image image)
		{
			var ntHeader = image.NtHeader;
			if(Address >= ntHeader.ImageBase + ntHeader.ImageSize || Address < ntHeader.ImageBase)
				throw new IndexOutOfRangeException("This virtual address is outside of the Image bounds");
			return new RVA(Address - ntHeader.ImageBase);
		}

		public IntPtr ToIntPtr()
		{
			return new IntPtr((int)Address);
		}
		public override bool Equals(object obj)
		{
			VA item = obj as VA;
			if(item == null)
				return false;
			return Address.Equals(item.Address);
		}

		public static implicit operator IntPtr(VA va)
		{
			return va.ToIntPtr();
		}
		public static VA operator +(VA a, RVA b)
		{
			return new VA(a.Address + b.Address);
		}
		public static bool operator ==(VA a, VA b)
		{
			if(System.Object.ReferenceEquals(a, b))
				return true;
			if(((object)a == null) || ((object)b == null))
				return false;

			return a.Address == b.Address;
		}

		public static bool operator !=(VA a, VA b)
		{
			return !(a == b);
		}

		public override int GetHashCode()
		{
			return Address.GetHashCode();
		}

		public override string ToString()
		{
			return String.Format("{0:X}", Address);
		}
	}
	public class RVA
	{
		public UInt32 Address;

		public RVA(UInt32 address)
		{
			Address = address;
		}

		
		


		public override bool Equals(object obj)
		{
			RVA item = obj as RVA;
			if(item == null)
				return false;
			return Address.Equals(item.Address);
		}

		public static bool operator ==(RVA a, RVA b)
		{
			if(System.Object.ReferenceEquals(a, b))
				return true;
			if(((object)a == null) || ((object)b == null))
				return false;

			return a.Address == b.Address;
		}

		public static bool operator !=(RVA a, RVA b)
		{
			return !(a == b);
		}

		public override int GetHashCode()
		{
			return Address.GetHashCode();
		}

		public override string ToString()
		{
			return String.Format("{0:X}", Address);
		}
	}

	public class ImageOffset
	{
		public uint Address;
		public Image Image;

		public ImageOffset(Image image, uint address)
		{
			this.Address = address;
			this.Image = image;
		}
		Tuple<Image, UInt32> GetImageAddress()
		{
			return Tuple.Create(Image, Address);
		}

		public override bool Equals(object obj)
		{
			ImageOffset item = obj as ImageOffset;
			if(item == null)
				return false;
			return GetImageAddress().Equals(item.GetImageAddress());
		}
		public static bool operator ==(ImageOffset a, ImageOffset b)
		{
			if(System.Object.ReferenceEquals(a, b))
				return true;
			if(((object)a == null) || ((object)b == null))
				return false;
			return a.GetImageAddress() == b.GetImageAddress();
		}

		public static bool operator !=(ImageOffset a, ImageOffset b)
		{
			return !(a == b);
		}

		public override int GetHashCode()
		{
			return GetImageAddress().GetHashCode();
		}

		public Section GetSection()
		{
			var sections = Image.Sections;
			for(int i = 0 ; i < sections.Length ; i++)
			{
				var section = sections[i];
				if(Address >= section.PointerToRawData && (Address < section.PointerToRawData + section.SizeOfRawData))
					return section;
			}
			return null;
		}

		public RVA ToRVA()
		{
			var section = GetSection();
			var sectionAddress = Address - section.PointerToRawData;
			var rva = section.VirtualAddress + sectionAddress;
			if(rva >= section.VirtualAddress + section.VirtualSize)
				throw new InvalidOperationException("This Offset exists only on file");
			return new RVA(rva);
		}
	}
}
